package cn.assumejson.springframework.beans.factory.config;

import cn.assumejson.springframework.beans.factory.BeansException;
import cn.assumejson.springframework.beans.factory.DisposableBean;
import cn.assumejson.springframework.beans.factory.ObjectFactory;
import cn.hutool.core.lang.Assert;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 对单例Bean 进行CRUD
 *
 * @author Mr. Li
 * @version 1.0
 * @since 1.0 2021/11/30 7:15
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    protected static final Object NULL_OBJECT = new Object();

    /**
     * 单例池
     */
    private final Map<String, Object> singletonObjects = new HashMap<>();

    /**
     * 销毁池
     */
    private final Map<String, DisposableBean> disposableBeans = new LinkedHashMap<>();

    /**
     * 解决循环依赖 标记池
     */
    private final Set<String> singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap<>(16));

    /**
     * 二级缓存 存储早期 没有走完完整额Bean 生命周期的Bean
     */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    /**
     * 第三级缓存
     */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    /**
     * 已注册的单例名称set
     */
    private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

    @Override
    public Object getSingleton(String beanName) {
        //在这里 系统一般是允许早期对象引用的 allowEarlyReference通过这个参数可以控制解决循环依赖
        return getSingleton(beanName, true);
    }

    @Override
    public Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 尝试先从一级缓存中获取
        Object singletonObject = this.singletonObjects.get(beanName);

        // 判断当前对象是否出现循环依赖的问题
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            // 如果二级缓存中还没有该bean，那么就走最后一道保险，第三级缓存。创建Bean
            if (singletonObject == null && allowEarlyReference) {
                synchronized (this.singletonObjects) {
                    // 最后尝试获取一次
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                            // 使用第三级缓存
                            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                singletonObject = singletonFactory.getObject();
                                // 存入到 二级缓存中，因为该Bean 也还没有走完所有的Bean生命周期
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }

        }
        return singletonObject;
    }

    public void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");

        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                //加入到三级缓存中，暴露早期对象用于解决循环依赖
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }
    }

    public void destroySingleton(String beanName) {
        //从缓存中移除当前bean的相关信息,由于不知道在哪里发生异常，所以我们把跟当前bean的所有缓存记录都清除
        removeSingleton(beanName);

        //创建一个变量用于接受 实现了DisposableBean接口的对象变量
        DisposableBean disposableBean;
        synchronized (this.disposableBeans) {
            disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
        }
        //进行bean的销毁
        destroyBean(beanName, disposableBean);
    }

    private void destroyBean(String beanName, DisposableBean disposableBean) {
        try {
            if (disposableBean != null) {
                disposableBean.destroy();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void removeSingleton(String beanName) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.remove(beanName);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.remove(beanName);
        }
    }

    public boolean isSingletonCurrentlyInCreation(String beanName) {
        return singletonsCurrentlyInCreation.contains(beanName);
    }

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");
        synchronized (this.singletonObjects) {
            Object singletonObject = singletonObjects.get(beanName);
            if (null == singletonObject) {
                beforeSingletonCreation(beanName);
                try {
                    // 调用lambda 表达式初始化Bean
                    singletonObject = singletonFactory.getObject();
                } catch (Exception ex) {
                    throw new BeansException("lambda running fail.{}", ex);

                }
                // 将正在创建的标记从集合中移除
                afterSingletonCreation(beanName);

                addSingleton(beanName, singletonObject);
            }
            return singletonObject;
        }
    }

    private void afterSingletonCreation(String beanName) {
        if (this.singletonsCurrentlyInCreation.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
            throw new BeansException("Error creating singleton bean, marking");
        }
    }

    // 将正在创建的Bean 进行标记
    private void beforeSingletonCreation(String beanName) {
        if (!this.singletonsCurrentlyInCreation.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeansException("Error creating singleton bean, marking");
        }
    }

    @Override
    public boolean containsSingleton(String beanName) {
        return singletonObjects.containsKey(beanName);
    }

    /**
     * 添加单例对象模板
     *
     * @param beanName        beanName
     * @param singletonObject singletonObject
     */
    @Override
    public void registerSingleton(String beanName, Object singletonObject) {
        addSingleton(beanName, singletonObject);
    }

    /**
     * 将生成的单例bean  存入到单例池中
     *
     * @param beanName        beanName
     * @param singletonObject 单例对象
     */
    private void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            //加入到单例缓存池中
            this.singletonObjects.put(beanName, singletonObject);
            //从三级缓存中移除(针对的不是处理循环依赖的)
            this.singletonFactories.remove(beanName);
            //从二级缓存中移除(循环依赖的时候 早期对象存在于二级缓存)
            this.earlySingletonObjects.remove(beanName);
            //用来记录保存已经处理的bean
            this.registeredSingletons.add(beanName);
        }
    }

    public void registerDisposableBean(String beanName, DisposableBean bean) {
        synchronized (this.disposableBeans) {
            this.disposableBeans.put(beanName, bean);
        }
    }

    @Override
    public void destroySingletons() {
        Set<String> keySet = this.disposableBeans.keySet();
        String[] beanNames = keySet.toArray(new String[0]);

        Arrays.stream(beanNames).forEach(s -> {
            DisposableBean disposableBean = disposableBeans.remove(s);
            try {
                disposableBean.destroy();
            } catch (Exception e) {
                throw new BeansException("destroy run fail");
            }
        });
    }

    @Override
    public Object getSingletonMutex() {
        return this.singletonObjects;
    }
}
